﻿using FPSO.HMI.Views;
using FPSO.Models.Constant;
using FPSO.Models.Domain;
using FPSO.Models.ViewModel.OPC;
using Opc.Ua;
using OpcUaHelper;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace FPSO.HMI.Utils
{
    public class OPCUtil
    {
        //private OpcUaClient uaClient;

        public OPCUtil()
        {
            //this.uaClient = null;
        }

        public void GetOpcValues(List<OPCTag> tagList)
        {
            //首先将所有值清空
            foreach (OPCTag tag in tagList)
            {
                tag.OpcValue = null;
            }

            try
            {
                if (App.UaClient == null)
                {
					App.UaClient = new OpcUaClient();
					App.UaClient.ConnectServer(SysConfig.OPCConnStr, SysConfig.OPCBackupConnStr).Wait();
                }

                const int BATCH = 100;
                int batch = BATCH;
                for (int i = 0; i < tagList.Count; i += batch)
                {
                    batch = (tagList.Count - i) > BATCH ? BATCH : tagList.Count - i;
                    NodeId[] nodeIds = new NodeId[batch];
                    for (int j = 0; j < batch; j++)
                    {
                        //nodeIds[j] = new NodeId(string.Format("ns=2;s={0}", tagList[i + j].OpcName));
						nodeIds[j] = new NodeId(string.Format("{0}{1}", SysConfig.OPC_ADD_PRE, tagList[i + j].OpcName));
					}

					try
					{
						List<DataValue> dataList = App.UaClient.ReadNodes(nodeIds);
						for (int j = 0; j < batch; j++)
						{
							tagList[i + j].OpcValue = dataList[j];
						}
					}
					catch (Exception ex) 
					{
					}
				}
            }
            catch(Exception ex)
            {
				CommUtil.AddSystemLog(0, 2, ex.Message);
			}
		}

        public bool SetOpcValues(Dictionary<string, object> valueDic)
        {
            try
            {
                if (App.UaClient == null)
                {
					App.UaClient = new OpcUaClient();
					App.UaClient.ConnectServer(SysConfig.OPCConnStr, SysConfig.OPCBackupConnStr).Wait();
                }

                string[] tags = new string[valueDic.Count];
                object[] vals = new object[valueDic.Count];
                int i = 0;
                foreach(var tv in valueDic)
                {
                    tags[i] = tv.Key;
                    vals[i] = tv.Value;
                    i++;
                }
                return App.UaClient.WriteNodes(tags, vals);
            }
            catch (Exception ex)
            {
				App.UaClient = null;
                throw ex;
            }
        }


        //对任务功能的封装

        /// <summary>
        /// 获取任务指令缓存中的数据
        /// </summary>
        /// <param name="queueId">队列号 1,2,3</param>
        /// <param name="isCOT">true 货油舱，false压载舱</param>
        /// <returns>指令状态</returns>
        public List<OPCTaskInstruct> GetTaskInstructCache(int queueId, bool isCOT)
        {
            string preTagFmt = "Task{0}{1}TInstructCache[{2}].{3}";
            string typeStr = isCOT ? "C" : "B";

            if (queueId != TaskQueueConstant.LOAD_TASK_1_ID
                && queueId != TaskQueueConstant.LOAD_TASK_2_ID
                && queueId != TaskQueueConstant.UNLOAD_TASK_1_ID)
            {
                throw new Exception("任务队列错误");
            }

            List<OPCTag> opcTags = new List<OPCTag>();
            const int CACHE_COUNT = 2;
            const int TAGS_PER_INSTRUCT = 6;

            for (int i = 0; i <= CACHE_COUNT; i++)
            {
                opcTags.Add(new OPCTag() { OpcName = string.Format(preTagFmt, queueId, typeStr, i, "InstructAddress") });
                opcTags.Add(new OPCTag() { OpcName = string.Format(preTagFmt, queueId, typeStr, i, "TkNo") });
                opcTags.Add(new OPCTag() { OpcName = string.Format(preTagFmt, queueId, typeStr, i, "TkType") });
                opcTags.Add(new OPCTag() { OpcName = string.Format(preTagFmt, queueId, typeStr, i, "TargetLevel") });
                opcTags.Add(new OPCTag() { OpcName = string.Format(preTagFmt, queueId, typeStr, i, "TaskOp") });
                opcTags.Add(new OPCTag() { OpcName = string.Format(preTagFmt, queueId, typeStr, i, "ReadFlag") });
            }

            List<OPCTaskInstruct> instructList = new List<OPCTaskInstruct>();
            GetOpcValues(opcTags);
            for (int i = 0; i <= CACHE_COUNT; i++)
            {
                bool nullFlag = false;
                for (int j = 0; j < TAGS_PER_INSTRUCT; j++)
                {
                    if (opcTags[i * TAGS_PER_INSTRUCT + j].OpcValue.Value != null)
                    {
                        nullFlag = true;
                    }
                }

                if (!nullFlag)
                {
                    OPCTaskInstruct instruct = new OPCTaskInstruct();
                    instruct.InstructAddress = Convert.ToInt32(opcTags[i * TAGS_PER_INSTRUCT].OpcValue.Value);
                    instruct.TkNo = Convert.ToInt32(opcTags[i * TAGS_PER_INSTRUCT + 1].OpcValue.Value);
                    instruct.TkType = Convert.ToInt32(opcTags[i * TAGS_PER_INSTRUCT + 2].OpcValue.Value);
                    instruct.TargetLevel = Convert.ToInt32(opcTags[i * TAGS_PER_INSTRUCT + 3].OpcValue.Value);
                    instruct.TaskOp = Convert.ToInt32(opcTags[i * TAGS_PER_INSTRUCT + 3].OpcValue.Value);
                    instruct.ReadFlag = Convert.ToInt32(opcTags[i * TAGS_PER_INSTRUCT + 3].OpcValue.Value);
                    instructList.Add(instruct);
                }
            }

            return instructList;
        }

        /// <summary>
        /// 写入任务指令缓存
        /// </summary>
        /// <param name="queueId"></param>
        /// <param name="isCOT"></param>
        /// <param name="instructList"></param>
        /// <returns></returns>
        public bool SaveTaskInstructCache(int queueId, bool isCOT, List<InstructLog> insLogList)
        {
            const int CACHE_COUNT = 2;
            string preTagFmt = "ns=2;s=Task{0}{1}TInstructCache[{2}].{3}";
            string typeStr = isCOT ? "C" : "B";
            if (queueId != TaskQueueConstant.LOAD_TASK_1_ID
                && queueId != TaskQueueConstant.LOAD_TASK_2_ID
                && queueId != TaskQueueConstant.UNLOAD_TASK_1_ID)
            {
                throw new Exception("任务队列错误");
            }
            if (insLogList.Count > CACHE_COUNT)
            {
                throw new Exception("指令数量超过缓存区大小");
            }

            Dictionary<string, object> tagValDic = new Dictionary<string, object>();
            for(int i = 0; i < insLogList.Count; i++)
            {
                InstructLog insLog = insLogList[i];
                int tkNo = insLog.TaskId;
                int tkType = isCOT ? 0 : 1;
                int taskOp = 1;
                if (insLog.TaskType == TaskTypeConstant.LOAD_ID)
                {
                    taskOp = 1;
                }
                else if (insLog.TaskType == TaskTypeConstant.UNLOAD_ID)
                {
                    taskOp = 2;
                }
                else if (insLog.TaskType == TaskTypeConstant.SWAP_ID)
                {
                    taskOp = 3;
                }
                tagValDic.Add(string.Format(preTagFmt, queueId, typeStr, i, "InstructAddress"), 0); // todo:这里需要确定InstructAddress怎么取值
                tagValDic.Add(string.Format(preTagFmt, queueId, typeStr, i, "sourceTkNo"), tkNo);
                tagValDic.Add(string.Format(preTagFmt, queueId, typeStr, i, "TkType"), tkType);
                tagValDic.Add(string.Format(preTagFmt, queueId, typeStr, i, "TargetLevel"), (float)insLog.TargetLevel);
                tagValDic.Add(string.Format(preTagFmt, queueId, typeStr, i, "TaskOp"), taskOp);
                tagValDic.Add(string.Format(preTagFmt, queueId, typeStr, i, "ReadFlag"), 2);
            }

            return SetOpcValues(tagValDic);
        }
    }
}
